package actors

import java.io.File
import java.nio.file.Files

import akka.actor.{Actor, ActorSystem, PoisonPill}
import akka.stream.Materializer
import dao._
import javax.inject.Inject
import models.Tables._
import org.apache.commons.io.FileUtils
import org.joda.time.DateTime
import play.api.libs.json.Json
import play.api.libs.ws.WSClient
import play.api.mvc.RequestHeader
import tool.Pojo._
import tool._
import utils.{CommandExecutor, MissionExecutor, Utils}

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}
import utils.Implicits._
import implicits.Implicits._
import scala.collection.parallel.CollectionConverters._


/**
 * Created by Administrator on 2019/10/24
 */
class MissionExecActor @Inject()(mission: MissionRow)(implicit val system: ActorSystem,
                                                      implicit val materializer: Materializer,
                                                      implicit val missionDao: MissionDao,
                                                      implicit val userDao: UserDao,
                                                      implicit val tool: Tool,
                                                      implicit val ws: WSClient,
                                                      implicit val dao: MyDao,
                                                      implicit val modeDao: ModeDao
) extends Actor {

  val testSteps: List[MyStep] = List(
    Step0
    //    ,StepPca,
    //    , StepBasic
    , StepUni(isTest = true)
    , StepOplsda(isTest = true)
    //    ,     StepPlsda,
    , StepPotential(isTest = false)
    //    , StepCor
//    , StepDignose
    //    ,StepPathway()

  )

  val steps = Tool.getSteps(testSteps)
  println(steps)

  override def receive: Receive = {
    case "run" =>
      val newMision = mission.copy(state = "running")
      missionDao.update(newMision).map { x =>
        val userId = mission.userId
        val outDir = Tool.getUserMissionDir(mission.userId)
        val missionExecutor = new MissionExecutor(mission.id, outDir)
        val (workspaceDir, resultDir) = (missionExecutor.workspaceDir, missionExecutor.resultDir)
        val f = Future {

        }
        f.flatMap { x =>
          val logFile = new File(workspaceDir.getParent, "log.txt")
          val commandExecutor = new CommandExecutor(logFile)
          val configFile = new File(workspaceDir, "config.txt")
          val configData = Tool.getConfigData(configFile)
          implicit val myInput = Tool.getMyInput(workspaceDir, configData)
          Tool.produceKeggInfoFile(myInput.rawDataFile, workspaceDir)
          tool.producePairsInfoFile(myInput.rawDataFile, workspaceDir, configData.species)
          val dataResultDir = new File(resultDir, "data_result")
          implicit val myResultDir = Tool.getMyResultDir(dataResultDir)
          implicit val myWorkspaceDir = Tool.getMyWorkspaceDir(workspaceDir)
          commandExecutor.exec { () =>
            Tool.stepPre(workspaceDir)
          }.map { () =>
            implicit val myWorkspaceDir = workspaceDir
            Tool.stepPreCopy
          }.mapThis { inCommand =>
            Tool.stepQC(inCommand)
          }.map { () =>
            myInput.compareFile.csvLines.drop(1).par.threadNum(configData.threadNum).foreach { columns =>
              implicit val dirName = columns(0)
              val dir = Tool.createAndReturnDir(workspaceDir, dirName)
              val resultDir = Tool.createAndReturnDir(myResultDir.treatDir, dirName)
              implicit val myInWorkspace = Tool.getMyInWorkspaceDir(dir)
              implicit val myInResultDir = Tool.getMyInResultDir(resultDir, dirName, configData)
              val myInInput = Tool.getMyInInput(columns, configData, myInput, workspaceDir, myInWorkspace.dir0)
              commandExecutor.mapThis { inCommand =>
                val missionTool = new MissionTool(myInInput, myInWorkspace, ws, configData)
                missionTool.configStep(steps, inCommand)
              }
            }
          }.map { () =>
            commandExecutor.exec { () =>
              Tool.treatVenn(myWorkspaceDir.inDirTreat.smsDir)
            }.map { () =>
              implicit val workspaceDir = myWorkspaceDir.inDirTreat.smsDir
              Tool.treatVennCopy
            }.exec { () =>
              Tool.csVenn(myWorkspaceDir.inDirTreat.csDir)
            }.map { () =>
              implicit val workspaceDir = myWorkspaceDir.inDirTreat.csDir
              Tool.csVennCopy
            }.exec { () =>
              val sourceDir = new File(resultDir, s"${Tool.reportDirName}/sources")
              FileUtils.copyDirectory(dataResultDir, sourceDir)
              ReportTool.dirSvg2png(sourceDir)
              val dir = new File(workspaceDir, "pdf_to_png").createDirectoryWhenNoExist
              ReportTool.pdf2pngsFast(dir, sourceDir)
            }.map { () =>
              ReportTool.produceZip(mission)
            }
          }

          val state = if (commandExecutor.isSuccess) {
            val sourceDir = new File(resultDir, s"${Tool.reportDirName}/sources")
            val treatDir = new File(sourceDir, "Treatment")
            val warnings = myInput.compareFile.csvLines.drop(1).map { columns =>
              val dirName = columns(0)
              val potentialDir = new File(treatDir, s"${dirName}").listFiles().find(_.getName.contains("Potential_Biomarkers")).get
              val markersFile = new File(potentialDir, "Markers.csv")
              val size = markersFile.csvLines.notEmptyLines.drop(1).size
              if (size == 0) {
                s"${dirName} 不存在潜在生物标志物！"
              } else ""
            }.notEmptyLines
            if (warnings.nonEmpty) {
              warnings.toFile(logFile)
              "warning"
            } else "success"
          } else {
            "error"
          }
          val newMission = mission.copy(state = state, endTime = Some(new DateTime()))
          missionDao.update(newMission).map { x =>
            Utils.deletePidFile(workspaceDir)
          }
        }.onComplete {
          case Failure(exception) =>
            exception.printStackTrace()
            FileUtils.writeStringToFile(missionExecutor.logFile, exception.toString)
            val newMission = mission.copy(state = "error", endTime = Some(new DateTime()))
            missionDao.update(newMission).map { x =>
              Utils.deletePidFile(workspaceDir)
            }
            self ! "stop"
          case Success(x) =>
            self ! "stop"
        }

      }

    case "stop" =>
      self ! PoisonPill

  }
}
